package com.hk.common.shiro.filter;


import com.hk.common.shiro.token.JwtToken;
import com.hk.common.shiro.token.JwtTokenUtil;
import com.hk.common.utils.ResponseUtils;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @Description: 鉴权登录拦截器
 * @Author: wj
 * @Date: 2019/10/30
 * <p>
 * <p>
 * 执行顺序 preHandle -> isAccessAllowed -> isLoginAttempt -> executeLogin
 **/
@Component
public class JwtFilter extends BasicHttpAuthenticationFilter {

    private final Logger log = LoggerFactory.getLogger(JwtFilter.class);

    private JwtTokenUtil jwtTokenUtil;

    public JwtFilter(JwtTokenUtil jwtTokenUtil){
        this.jwtTokenUtil=jwtTokenUtil;
    }

    @Override
    protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
        return getRequestToken((HttpServletRequest) request) != null;
    }

    @Override
    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
        return new JwtToken(getRequestToken((HttpServletRequest) request));
    }

    /**
     * 执行登录认证
     *
     * @param request
     * @param response
     * @param mappedValue
     * @return
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        if (isLoginAttempt(request, response)) {
            try {
                return executeLogin(request, response);
            } catch (Exception e) {
                log.error("isAccessAllowed 异常",e);
                ResponseUtils.write(httpServletResponse, ResponseEntity.status(401).body(e.getMessage()));
                return false;
            }
        }
        return false;
    }

    /**
     * 执行登录
     */
    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) {
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        String authzHeader = getRequestToken((HttpServletRequest) request);
        getSubject(request, response).login(this.createToken(request, response));
        //检查token过期时间超过配置时间分钟刷新token：jwt.refreshCheckTime
        if( refreshCheck(authzHeader, System.currentTimeMillis())){
            String token = jwtTokenUtil.generateToken(jwtTokenUtil.getUserName(authzHeader));
            httpServletResponse.setHeader("Access-Control-Expose-Headers", "Authorization");
            httpServletResponse.setHeader("Authorization",token);
        }
        return true;
    }

    /**
     * 检查是否需要更新Token
     *
     * @param authorization
     * @param currentTimeMillis
     * @return
     */
    private boolean refreshCheck(String authorization, Long currentTimeMillis) {
        Long tokenMillis = jwtTokenUtil.getExp(authorization);
        if(tokenMillis!=null){
            if (currentTimeMillis - tokenMillis> ( 30 * 1000L)) {
                return true;
            }
        }
       return false;
    }


    /**
     * 对跨域提供支持
     */
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {

        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
        httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
        httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
        // 跨域时会首先发送一个option请求，这里我们给option请求直接返回正常状态
        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            httpServletResponse.setStatus(HttpStatus.OK.value());
            return false;
        }
        return super.preHandle(request, response);
    }

    /**
     * 重写 onAccessDenied 方法，避免父类中调用再次executeLogin
     *
     * @param request
     * @param response
     * @return
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) {
//        super方法会写  httpResponse.setHeader("WWW-Authenticate", authcHeader); 导致前端出现原生登录弹框
//        this.sendChallenge(request,response);
        HttpServletResponse httpResponse = WebUtils.toHttp(response);
        httpResponse.setStatus(401);
        return false;
    }

    /**
     * 获取请求的token
     */
    private String getRequestToken(HttpServletRequest httpRequest){
        //从header中获取token
        String token = httpRequest.getHeader("Authorization");
        //如果header中不存在token，则从参数中获取token
        if(StringUtils.isEmpty(token)){
            token = httpRequest.getParameter("Authorization");
        }
        return token;
    }

}
